﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yuans.BinaryConfig;
using Yuans.BinaryConfig.BlobIO;
using Yuans.BinaryConfig.Utils;
using Rhetos.Utilities;
using System.Reflection;

namespace Yuans.BinaryConfig
{
    public class ConfigManager
    {
        private List<ConfigItem> mConfigList;

        public List<ConfigItem> ConfigList
        {
            get { return mConfigList; }
            set { mConfigList = value; }
        }

        public ConfigManager(List<ConfigItem> list)
        {
            mConfigList = list;
        }
        public void GenTestItems()
        {
            mConfigList.Add(new BoolItem("TTT", false));
            mConfigList.Add(new StringItem("Author", "Yuan"));
            mConfigList.Add(new DateTimeItem("EditDateTime", DateTime.Now));
            mConfigList.Add(new StringItem("ProductName", "The ONE Piano PRO"));
            mConfigList.Add(new NumberItem("A2sVer", 0x00010000, ConfigItem.ParamType.NUMBER_U32));
            mConfigList.Add(new NumberItem("Pic32Ver", 0x00010300, ConfigItem.ParamType.NUMBER_U32));
            mConfigList.Add(new NumberItem("Tas57xxSpkVol", 0x3434, ConfigItem.ParamType.NUMBER_U16));
            mConfigList.Add(new NumberItem("Tas57xxHpVol", 0x55, ConfigItem.ParamType.NUMBER_U16));
            mConfigList.Add(new NumberItem("UsbAudioVol", 0x55, ConfigItem.ParamType.NUMBER_U8));
            mConfigList.Add(new NumberItem("AuxInVol", 0x55, ConfigItem.ParamType.NUMBER_U8));
            mConfigList.Add(new StringItem("VelCurveNote", "Tune by HuaXin"));
            mConfigList.Add(new BlobItem("VelCurveNormalW", new byte[1] { 0x23 }));
            mConfigList.Add(new BlobItem("VelCurveNormalB", new byte[128]));
        }
        public void LoadConfigFromFile(string filePath)
        {
            if (File.Exists(filePath))
            {
                using (BinaryReader fileReader = new BinaryReader(File.Open(filePath, FileMode.Open)))
                {
                    string singleConfigName;
                    ConfigItem.ParamType paramType;
                    int valueLength;
                    int nameStrLen;
                    UInt32 hashValue;
                    UInt32 configRawBytesLen;
                    UInt32 fileIdentifier;

                    mConfigList.Clear();

                    fileIdentifier = fileReader.ReadUInt32();

                    configRawBytesLen = fileReader.ReadUInt32();
                    hashValue = fileReader.ReadUInt32();

                    if (fileIdentifier != BitConverter.ToUInt32(Encoding.ASCII.GetBytes("YCFG"), 0))
                    {
                        return;
                    }

                    do
                    {
                        nameStrLen = fileReader.ReadByte();

                        singleConfigName = System.Text.Encoding.ASCII.GetString(fileReader.ReadBytes(nameStrLen));

                        paramType = (ConfigItem.ParamType)fileReader.ReadByte();

                        if (ConfigItem.GetItemLengthByteNum(paramType) == 2)
                        {
                            valueLength = fileReader.ReadUInt16();
                        }
                        else
                        {
                            valueLength = fileReader.ReadByte();
                        }

                        switch (paramType)
                        {
                            case ConfigItem.ParamType.NUMBER_U8:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, fileReader.ReadByte(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.NUMBER_I8:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, (sbyte)fileReader.ReadByte(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.NUMBER_U16:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, fileReader.ReadUInt16(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.NUMBER_I16:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, fileReader.ReadInt16(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.NUMBER_U32:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, fileReader.ReadUInt32(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.NUMBER_I32:
                                {
                                    mConfigList.Add(new NumberItem(singleConfigName, fileReader.ReadInt32(), paramType));
                                }
                                break;
                            case ConfigItem.ParamType.BOOL:
                                {
                                    mConfigList.Add(new BoolItem(singleConfigName, fileReader.ReadBoolean()));
                                }
                                break;
                            case ConfigItem.ParamType.STRING:
                                {
                                    string stringValue;

                                    stringValue = System.Text.Encoding.ASCII.GetString(fileReader.ReadBytes(valueLength));

                                    mConfigList.Add(new StringItem(singleConfigName, stringValue.Substring(0, stringValue.Length - 1)));
                                }
                                break;
                            case ConfigItem.ParamType.BLOB:
                                {
                                    byte[] blobValue;

                                    blobValue = fileReader.ReadBytes(valueLength);

                                    mConfigList.Add(new BlobItem(singleConfigName, blobValue));
                                }
                                break;
                            case ConfigItem.ParamType.DATETIME:
                                {
                                    DateTime dateTimeValue;

                                    int year, month, day, hour, min, sec;

                                    year = fileReader.ReadUInt16();
                                    month = fileReader.ReadByte();
                                    day = fileReader.ReadByte();
                                    hour = fileReader.ReadByte();
                                    min = fileReader.ReadByte();
                                    sec = fileReader.ReadByte();

                                    dateTimeValue = new DateTime(year, month, day, hour, min, sec);

                                    mConfigList.Add(new DateTimeItem(singleConfigName, dateTimeValue));
                                }
                                break;
                            default:
                                break;
                        }
                    } while (fileReader.BaseStream.Position < fileReader.BaseStream.Length);
                }
            }
        }
        public void WriteConfigToFile(string filePath)
        {
            using (BinaryWriter fileWriter = new BinaryWriter(File.Open("temp", FileMode.Create)))
            {
                foreach (ConfigItem configItem in mConfigList)
                {
                    fileWriter.Write(configItem.GetItemRawBytes());
                }
            }

            byte[] configRawBytes = File.ReadAllBytes("temp");
            File.Delete("temp");
            UInt32 hashValue = HashMethod.SuperFastHash(configRawBytes);
            UInt32 configRawBytesSize = (UInt32)configRawBytes.Length;
            UInt32 fileIdentifier = BitConverter.ToUInt32(Encoding.ASCII.GetBytes("YCFG"), 0);

            using (BinaryWriter fileWriter = new BinaryWriter(File.Open(filePath, FileMode.Create)))
            {
                fileWriter.Write(fileIdentifier);
                fileWriter.Write(configRawBytesSize);
                fileWriter.Write(hashValue);
                fileWriter.Write(configRawBytes);
            }

        }
        public void GenCStyleSourceCode(string templateFilePath, string filePath)
        {
            string templateString = File.ReadAllText(templateFilePath);

            StringBuilder configInfo = new StringBuilder();

            int configDataLen;

            configInfo.AppendLine(TextOutPutGadget.GenLine());

            configInfo.AppendLine(TextOutPutGadget.GenRowString(new string[] { "Key Name", "Key Type", "Key Value" }));
            configInfo.AppendLine(TextOutPutGadget.GenLine());

            foreach (ConfigItem configItem in mConfigList)
            {
                configInfo.AppendLine(TextOutPutGadget.GenRowString(new string[] { configItem.ItemName, configItem.ItemType.ToString(), configItem.GetFriendValueDescription() }));
                configInfo.AppendLine(TextOutPutGadget.GenLine());
            }

            String configData;

            WriteConfigToFile("temp");

            byte[] configDataBytes = File.ReadAllBytes("temp");

            configDataLen = configDataBytes.Length;

            configData = TextOutPutGadget.GenByteArray(configDataBytes, 10);

            File.Delete("temp");



            // Tokens will be delimited with { and }.
            FastReplacer fr = new FastReplacer("#", "@", false);
            fr.Append(templateString);

            fr.Replace("#ConfigInfo@", configInfo.ToString());
            fr.Replace("#ConfigData@", configData);
            fr.Replace("#ConfigDataByteLen@", configDataLen.ToString());
            fr.Replace("#GenDateTime@", DateTime.Now.ToString());

            using (StreamWriter fileWriter = new StreamWriter(filePath, false))
            {
                fileWriter.Write(fr.ToString());
            }

        }
        public void GenCStyleSourceCode(string filePath)
        {
            File.WriteAllText("tempDefaultTemplateFile", BinaryConfig.MainResource.DefaultSourceTemplate,Encoding.Default);
            GenCStyleSourceCode("tempDefaultTemplateFile", filePath);
            File.Delete("tempDefaultTemplateFile");
        }
    }
}